add support for conditional icns gdk-pixbuf loader (Closes: #395738)
authorBastien Nocera <hadess@hadess.net>
Tue, 20 Nov 2007 10:26:57 +0000 (10:26 +0000)
committerBastien Nocera <hadess@src.gnome.org>
Tue, 20 Nov 2007 10:26:57 +0000 (10:26 +0000)
2007-11-20  Bastien Nocera  <hadess@hadess.net>

* configure.in: add support for conditional icns gdk-pixbuf loader
(Closes: #395738)

2007-11-20  Bastien Nocera  <hadess@hadess.net>

* Makefile.am:
* io-icns.c: Add icns (MacOS X icons) loader, based on work by
Lyonel Vincent <lyonel@ezix.org> (Closes: #395738)

svn path=/trunk/; revision=19007

ChangeLog
configure.in
gdk-pixbuf/ChangeLog
gdk-pixbuf/Makefile.am
gdk-pixbuf/io-icns.c [new file with mode: 0644]

index 6bdc73283fe7418b91441fc36ca5572f409c6d89..1a248c4ddadadbd79c8fd8993e77f09486da2b99 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2007-11-20  Bastien Nocera  <hadess@hadess.net>
+
+       * configure.in: add support for conditional icns gdk-pixbuf loader
+       (Closes: #395738)
+
 2007-11-19 10:31:26  Tim Janik  <timj@imendio.com>
 
        * configure.in: updated version number to 2.15.0 for development.
index 32d6143e055adc456373a30ee1177c871a856e20..22f5781431baf3a6d871977ab43cbbc5c189b3fd 100644 (file)
@@ -886,7 +886,7 @@ else
    fi
 fi
 
-all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,xbm,tga,pcx"
+all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,xbm,tga,pcx,icns"
 included_loaders=""
 # If no loaders specified, include all
 if test "x$with_included_loaders" = xyes ; then
@@ -930,6 +930,7 @@ AM_CONDITIONAL(INCLUDE_XPM, [test x"$INCLUDE_xpm" = xyes])
 AM_CONDITIONAL(INCLUDE_XBM, [test x"$INCLUDE_xbm" = xyes])
 AM_CONDITIONAL(INCLUDE_TGA, [test x"$INCLUDE_tga" = xyes])
 AM_CONDITIONAL(INCLUDE_PCX, [test x"$INCLUDE_pcx" = xyes])
+AM_CONDITIONAL(INCLUDE_ICNS, [test x"$INCLUDE_icns" = xyes])
 
 AC_HEADER_SYS_WAIT
 
index c3cfcfddcf9152cbe085c07d3bb0aa554385b23e..dee96b8e76723062e1e05af6db9b74a47f8854a5 100644 (file)
@@ -1,3 +1,9 @@
+2007-11-20  Bastien Nocera  <hadess@hadess.net>
+
+       * Makefile.am:
+       * io-icns.c: Add icns (MacOS X icons) loader, based on work by
+       Lyonel Vincent <lyonel@ezix.org> (Closes: #395738)
+
 2007-11-10  Matthias Clasen <mclasen@redhat.com>
 
        * io-jpeg.c: Avoid unaligned accesses that cause
@@ -147,7 +153,7 @@ Sat Jul  7 17:34:33 2007  Tim Janik  <timj@gtk.org>
 
        * gdk-pixbuf-simple-anim.c (gdk_pixbuf_simple_anim_add_frame):
        Don't double the delay time of the first frame.  (#431997,
-       Björn Lindqvist)
+       Bj?rn Lindqvist)
 
 2007-04-25  Matthias Clasen <mclasen@redhat.com>
 
@@ -1583,7 +1589,7 @@ Mon Aug 18 10:25:29 2003  Owen Taylor  <otaylor@redhat.com>
 
        * pixops/pixops.c (correct_total): Split correction into multiple
        pieces if no single weight is large enough to apply the unsplit
-       correction.  (#117431, problem reported by Tomas Ögren) 
+       correction.  (#117431, problem reported by Tomas ?gren) 
 
 2003-07-13  Matthias Clasen  <maclas@gmx.de>
 
@@ -2371,7 +2377,7 @@ Wed Feb 27 18:33:04 2002  Owen Taylor  <otaylor@redhat.com>
 
        * gdk-pixdata.c (gdk_pixdata_to_csource): Use {} not
        () to group around string assigned to char[]. (#72767,
-       Tomas Ögren)
+       Tomas ?gren)
 
 2002-02-21  Havoc Pennington  <hp@pobox.com>
 
@@ -3594,7 +3600,7 @@ Wed Jun 21 16:02:48 2000  Owen Taylor  <otaylor@redhat.com>
 2000-06-05     Mathieu Lacage  <mathieu@gnome.org>
 
        * configure.in: add some gtk parameters to the
-       GDK_PIXBUF_LIBÃ�²S and GDK_PIXBUG_INCLUDEDIR vars. One more
+       GDK_PIXBUF_LIB?�?S and GDK_PIXBUG_INCLUDEDIR vars. One more
        fight in my crusade for strange prefix compile...
 
 2000-05-30  Not Zed  <NotZed@HelixCode.com>
@@ -3691,7 +3697,7 @@ Fri May  5 12:16:32 2000  Owen Taylor  <otaylor@redhat.com>
        * gdk-pixbuf/Makefile.am (INCLUDES): Add $(GNOME_CFLAGS).
        Reported by Jens Finke.
 
-2000-04-14 Tomasz KÃ�³opczko <kloczek@pld.org.pl>
+2000-04-14 Tomasz K?�?opczko <kloczek@pld.org.pl>
 
        * gdk-pixbuf/pixops/makefile.am: $(LIBART_CFLAGS) replaced by 
        $(GTK_CFLAGS) - now gdk-pixbuf compiles correctly.
index 683f3c9de2f0ee03916b16dfca396d125fcfea2d..4fb5e0b07f84420481a8a4505494634fc3addecd 100644 (file)
@@ -164,6 +164,14 @@ libpixbufloader_tga_la_SOURCES = io-tga.c
 libpixbufloader_tga_la_LDFLAGS = -avoid-version -module $(no_undefined)
 libpixbufloader_tga_la_LIBADD = $(module_libs)
 
+#
+# The .icns loader
+#
+libpixbufloader_static_icns_la_SOURCES = io-icns.c
+libpixbufloader_icns_la_SOURCES = io-icns.c
+libpixbufloader_icns_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libpixbufloader_icns_la_LIBADD = $(module_libs)
+
 #
 # The PCX loader
 #
@@ -262,6 +270,12 @@ else
 PCX_LIB = libpixbufloader-pcx.la
 endif
 
+if INCLUDE_ICNS
+STATIC_ICNS_LIB = libpixbufloader-static-icns.la
+else
+ICNS_LIB = libpixbufloader-icns.la
+endif
+
 if BUILD_DYNAMIC_MODULES
 
 loader_LTLIBRARIES =   \
@@ -278,6 +292,7 @@ loader_LTLIBRARIES =        \
        $(XPM_LIB)      \
        $(XBM_LIB)      \
        $(TGA_LIB)      \
+       $(ICNS_LIB)     \
        $(PCX_LIB)
 
 
@@ -297,6 +312,7 @@ noinst_LTLIBRARIES =                \
        $(STATIC_XPM_LIB)       \
        $(STATIC_XBM_LIB)       \
        $(STATIC_TGA_LIB)       \
+       $(STATIC_ICNS_LIB)      \
        $(STATIC_PCX_LIB)
 
 builtin_objs = @INCLUDED_LOADER_OBJ@
diff --git a/gdk-pixbuf/io-icns.c b/gdk-pixbuf/io-icns.c
new file mode 100644 (file)
index 0000000..98ad97d
--- /dev/null
@@ -0,0 +1,396 @@
+/* Mac OS X .icns icons loader
+ *
+ * Copyright (c) 2007 Lyonel Vincent <lyonel@ezix.org>
+ * Copyright (c) 2007 Bastien Nocera <hadess@hadess.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gdk-pixbuf-private.h"
+#include "gdk-pixbuf-io.h"
+
+G_MODULE_EXPORT void fill_vtable (GdkPixbufModule * module);
+G_MODULE_EXPORT void fill_info (GdkPixbufFormat * info);
+
+#define IN /**/
+#define OUT /**/
+#define INOUT /**/
+
+struct IcnsBlockHeader
+{
+  char id[4];
+  guint32 size;                        /* caution: bigendian */
+};
+typedef struct IcnsBlockHeader IcnsBlockHeader;
+
+/*
+ * load raw icon data from 'icns' resource
+ *
+ * returns TRUE when successful
+ */
+static gboolean
+load_resources (unsigned size, IN gpointer data, gsize datalen,
+               OUT guchar ** picture, OUT gsize * plen,
+               OUT guchar ** mask, OUT gsize * mlen)
+{
+  IcnsBlockHeader *header = NULL;
+  const char *bytes = NULL;
+  const char *current = NULL;
+  guint32 blocklen = 0;
+  guint32 icnslen = 0;
+  gboolean needs_mask = TRUE;
+
+  if (datalen < 2 * sizeof (guint32))
+    return FALSE;
+  if (!data)
+    return FALSE;
+
+  *picture = *mask = NULL;
+  *plen = *mlen = 0;
+
+  bytes = data;
+  header = (IcnsBlockHeader *) data;
+  if (memcmp (header->id, "icns", 4) != 0)
+    return FALSE;
+
+  icnslen = GUINT32_FROM_BE (header->size);
+  if ((icnslen > datalen) || (icnslen < 2 * sizeof (guint32)))
+    return FALSE;
+
+  current = bytes + sizeof (IcnsBlockHeader);
+  while ((current - bytes < icnslen) && (icnslen - (current - bytes) >= sizeof (IcnsBlockHeader)))
+    {
+      header = (IcnsBlockHeader *) current;
+      blocklen = GUINT32_FROM_BE (header->size);
+
+      /* Check that blocklen isn't garbage */
+      if (blocklen > icnslen - (current - bytes))
+        return FALSE;
+
+      switch (size)
+       {
+       case 256:
+          if (memcmp (header->id, "ic08", 4) == 0)     /* 256x256 icon */
+            {
+             *picture = (gpointer) (current + sizeof (IcnsBlockHeader));
+             *plen = blocklen - sizeof (IcnsBlockHeader);
+           }
+           needs_mask = FALSE;
+         break;
+       case 128:
+         if (memcmp (header->id, "it32", 4) == 0)      /* 128x128 icon */
+           {
+             *picture = (gpointer) (current + sizeof (IcnsBlockHeader));
+             *plen = blocklen - sizeof (IcnsBlockHeader);
+             if (memcmp (*picture, "\0\0\0\0", 4) == 0)
+               {
+                 *picture += 4;
+                 *plen -= 4;
+               }
+           }
+         if (memcmp (header->id, "t8mk", 4) == 0)      /* 128x128 mask */
+           {
+             *mask = (gpointer) (current + sizeof (IcnsBlockHeader));
+             *mlen = blocklen - sizeof (IcnsBlockHeader);
+           }
+         break;
+       case 48:
+         if (memcmp (header->id, "ih32", 4) == 0)      /* 48x48 icon */
+           {
+             *picture = (gpointer) (current + sizeof (IcnsBlockHeader));
+             *plen = blocklen - sizeof (IcnsBlockHeader);
+           }
+         if (memcmp (header->id, "h8mk", 4) == 0)      /* 48x48 mask */
+           {
+             *mask = (gpointer) (current + sizeof (IcnsBlockHeader));
+             *mlen = blocklen - sizeof (IcnsBlockHeader);
+           }
+         break;
+       case 32:
+         if (memcmp (header->id, "il32", 4) == 0)      /* 32x32 icon */
+           {
+             *picture = (gpointer) (current + sizeof (IcnsBlockHeader));
+             *plen = blocklen - sizeof (IcnsBlockHeader);
+           }
+         if (memcmp (header->id, "l8mk", 4) == 0)      /* 32x32 mask */
+           {
+             *mask = (gpointer) (current + sizeof (IcnsBlockHeader));
+             *mlen = blocklen - sizeof (IcnsBlockHeader);
+           }
+         break;
+       case 16:
+         if (memcmp (header->id, "is32", 4) == 0)      /* 16x16 icon */
+           {
+             *picture = (gpointer) (current + sizeof (IcnsBlockHeader));
+             *plen = blocklen - sizeof (IcnsBlockHeader);
+           }
+         if (memcmp (header->id, "s8mk", 4) == 0)      /* 16x16 mask */
+           {
+             *mask = (gpointer) (current + sizeof (IcnsBlockHeader));
+             *mlen = blocklen - sizeof (IcnsBlockHeader);
+           }
+         break;
+       default:
+         return FALSE;
+       }
+
+      current += blocklen;
+    }
+
+  if (!*picture)
+    return FALSE;
+  if (needs_mask && !*mask)
+    return FALSE;
+  return TRUE;
+}
+
+/*
+ * uncompress RLE-encoded bytes into RGBA scratch zone:
+ * if firstbyte >= 0x80, it indicates the number of identical bytes + 125
+ *     (repeated value is stored next: 1 byte)
+ * otherwise, it indicates the number of non-repeating bytes - 1
+ *     (non-repeating values are stored next: n bytes)
+ */
+static gboolean
+uncompress (unsigned size, INOUT guchar ** source, OUT guchar * target, INOUT gsize * _remaining)
+{
+  guchar *data = *source;
+  gsize remaining;
+  gsize i = 0;
+
+  /* The first time we're called, set remaining */
+  if (*_remaining == 0) {
+    remaining = size * size;
+  } else {
+    remaining = *_remaining;
+  }
+
+  while (remaining > 0)
+    {
+      guint8 count = 0;
+
+      if (data[0] & 0x80)      /* repeating byte */
+       {
+         count = data[0] - 125;
+
+         if (count > remaining)
+           return FALSE;
+
+         for (i = 0; i < count; i++)
+           {
+             *target = data[1];
+             target += 4;
+           }
+
+         data += 2;
+       }
+      else                     /* non-repeating bytes */
+       {
+         count = data[0] + 1;
+
+         if (count > remaining)
+           return FALSE;
+
+         for (i = 0; i < count; i++)
+           {
+             *target = data[i + 1];
+             target += 4;
+           }
+         data += count + 1;
+       }
+
+      remaining -= count;
+    }
+
+  *source = data;
+  *_remaining = remaining;
+  return TRUE;
+}
+
+static GdkPixbuf *
+load_icon (unsigned size, IN gpointer data, gsize datalen)
+{
+  guchar *icon = NULL;
+  guchar *mask = NULL;
+  gsize isize = 0, msize = 0, i;
+  guchar *image = NULL;
+
+  if (!load_resources (size, data, datalen, &icon, &isize, &mask, &msize))
+    return NULL;
+
+  /* 256x256 icons don't use RLE or uncompressed data,
+   * They're usually JPEG 2000 images */
+  if (size == 256)
+    {
+      GdkPixbufLoader *loader;
+      GdkPixbuf *pixbuf;
+
+      loader = gdk_pixbuf_loader_new ();
+      if (!gdk_pixbuf_loader_write (loader, icon, isize, NULL)
+         || !gdk_pixbuf_loader_close (loader, NULL))
+        {
+          g_object_unref (loader);
+          return NULL;
+       }
+
+      pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+      g_object_ref (pixbuf);
+      g_object_unref (loader);
+
+      return pixbuf;
+    }
+
+  g_assert (mask);
+
+  if (msize != size * size)    /* wrong mask size */
+    return NULL;
+
+  image = (guchar *) g_try_malloc0 (size * size * 4);  /* 4 bytes/pixel = RGBA */
+
+  if (!image)
+    return NULL;
+
+  if (isize == size * size * 4)        /* icon data is uncompressed */
+    for (i = 0; i < size * size; i++)  /* 4 bytes/pixel = ARGB (A: ignored) */
+      {
+       image[i * 4] = icon[4 * i + 1]; /* R */
+       image[i * 4 + 1] = icon[4 * i + 2];     /* G */
+       image[i * 4 + 2] = icon[4 * i + 3];     /* B */
+      }
+  else
+    {
+      guchar *data = icon;
+      gsize remaining = 0;
+
+      /* R */
+      if (!uncompress (size, &data, image, &remaining))
+        goto bail;
+      /* G */
+      if (!uncompress (size, &data, image + 1, &remaining))
+        goto bail;
+      /* B */
+      if (!uncompress (size, &data, image + 2, &remaining))
+        goto bail;
+    }
+
+  for (i = 0; i < size * size; i++)    /* copy mask to alpha channel */
+    image[i * 4 + 3] = mask[i];
+
+  return gdk_pixbuf_new_from_data ((guchar *) image, GDK_COLORSPACE_RGB,       /* RGB image */
+                                  TRUE,        /* with alpha channel */
+                                  8,   /* 8 bits per sample */
+                                  size,        /* width */
+                                  size,        /* height */
+                                  size * 4,    /* no gap between rows */
+                                  (GdkPixbufDestroyNotify)g_free,      /* free() function */
+                                  NULL);       /* param to free() function */
+
+bail:
+  g_free (image);
+  return NULL;
+}
+
+static int sizes[] = {
+  256, /* late-Tiger icons */
+  128, /* Standard OS X */
+  48,  /* Not very common */
+  32,  /* Standard Mac OS Classic (8 & 9) */
+  24,  /* OS X toolbars */
+  16   /* used in Mac OS Classic and dialog boxes */
+};
+
+static GdkPixbuf *
+icns_image_load (FILE *f, GError ** error)
+{
+  GByteArray *data;
+  GdkPixbuf *pixbuf = NULL;
+  guint i;
+
+  data = g_byte_array_new ();
+  while (!feof (f))
+    {
+      gint save_errno;
+      guchar buf[4096];
+      gsize bytes;
+
+      bytes = fread (buf, 1, sizeof (buf), f);
+      save_errno = errno;
+      data = g_byte_array_append (data, buf, bytes);
+
+      if (ferror (f))
+        {
+         g_set_error (error,
+                      G_FILE_ERROR,
+                      g_file_error_from_errno (save_errno),
+                      _("Error reading ICNS image: %s"),
+                      g_strerror (save_errno));
+
+         g_byte_array_free (data, TRUE);
+
+         return NULL;
+       }
+    }
+
+  for (i = 0; i < G_N_ELEMENTS(sizes) && !pixbuf; i++)
+    pixbuf = load_icon (sizes[i], data->data, data->len);
+
+  g_byte_array_free (data, TRUE);
+
+  if (!pixbuf)
+    g_set_error (error, GDK_PIXBUF_ERROR,
+                GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                _("Could not decode ICNS file"));
+
+  return pixbuf;
+}
+
+void
+fill_vtable (GdkPixbufModule * module)
+{
+  module->load = icns_image_load;
+}
+
+void
+fill_info (GdkPixbufFormat * info)
+{
+  static GdkPixbufModulePattern signature[] = {
+    {"icns", NULL, 100},       /* file begins with 'icns' */
+    {NULL, NULL, 0}
+  };
+  static gchar *mime_types[] = {
+    "image/x-icns",
+    NULL
+  };
+  static gchar *extensions[] = {
+    "icns",
+    NULL
+  };
+
+  info->name = "icns";
+  info->signature = signature;
+  info->description = N_("The ICNS image format");
+  info->mime_types = mime_types;
+  info->extensions = extensions;
+  info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
+  info->license = "GPL";
+  info->disabled = FALSE;
+}
+